RxJava + Retrofit 完成网络请求

前言

RxJava和Retrofit也火了一段时间了。一直没有时间研究这些新东西,最近有时间准备写个项目练手,打算先用Android写一个Demo出来,却发现Android的世界发生了天翻地覆的变化,EventBus和OKHttp3 都不见了,RxJava和Retrofit是什么东东?

如果你对Retrofit不熟悉就先看 官网的介绍

如果你对RxJava不熟悉请先看 官网介绍 以及 gank.io 给 Android 开发者的 RxJava 详解 这篇文章

当然也有很多RxJava与Retrofit的文章,我是参考tough1985大神的 这篇文章

学(好)习(记)就(性)要(不)做(如)个(烂)笔(笔)记(头)

RxJava如何与Retrofit结合

本文实现的目标

  1. RxJava如何与Retrofit结合
  2. 相同格式的Http请求数据该如何封装
  3. 相同格式的Http请求数据统一进行预处理
  4. 如何取消一个Http请求 – 观察者之间的对决,Oberver VS Subscriber
  5. 一个需要ProgressDialog的Subscriber该有的样子

添加依赖包

先贴出build.gradle文件的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.3.0'
compile 'com.android.support:support-v4:23.3.0'
compile 'com.android.support:recyclerview-v7:23.3.0'
//日志
compile 'com.orhanobut:logger:1.15'
//广告
compile 'com.bigkoo:convenientbanner:2.0.5'
compile 'com.ToxicBakery.viewpager.transforms:view-pager-transforms:1.2.32@aar'
//注解绑定view
compile 'com.jakewharton:butterknife:8.2.1'
apt 'com.jakewharton:butterknife-compiler:8.2.1'
//ViewPage指示器
compile 'com.astuetz:pagerslidingtabstrip:1.0.1'
//rx系列
compile 'io.reactivex:rxjava:1.1.6'
compile 'io.reactivex:rxandroid:1.2.1'
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:retrofit-adapters:2.1.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.google.code.gson:gson:2.7'
}

本文是基于RxJava 1.1.6Retrofit 2.1.0 来进行的。 添加rxandroid是因为rxjava中的线程问题。

如需查看项目代码 –> 代码地址:
https://github.com/Javen205/SmartCloud 选择Tag1

原生的Retrofit请求

添加Retrofit 接口

1
2
3
4
5
6
7
8
9
10
/**
* 请求示例:http://japi.juhe.cn/joke/content/list.from?
* key=您申请的KEY&page=2&pagesize=10&sort=asc&time=1418745237
*/
public interface JokeService {
//Retrofit原生请求
@GET("content/list.from")
Call<JokeEntity> getJokes(@QueryMap Map<String, Object> options);
}

Fragment 或者Activity中 调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
*
* 原生的Retrofit请求
*/
private void getJokes(Map<String, Object> options) {
String baseUrl="http://japi.juhe.cn/joke/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build();
JokeService jokeService = retrofit.create(JokeService.class);
Call<JokeEntity> call = jokeService.getJokes(options);
call.enqueue(new Callback<JokeEntity>() {
@Override
public void onResponse(Call<JokeEntity> call, Response<JokeEntity> response) {
JokeEntity jokeEntity = response.body();
Logger.i(jokeEntity.toString());
if(jokeEntity.getError_code()==0) {
List<JokeEntity.Data.Subject> data = jokeEntity.getResult().getData();
for (JokeEntity.Data.Subject subject : data) {
Logger.i(subject.toString());
}
textview.setText(data.get(0).toString());
}
}
@Override
public void onFailure(Call<JokeEntity> call, Throwable t) {
textview.setText(t.getMessage());
}
});
}

添加Rxjava

Retrofit本身对Rxjava提供了支持。

添加Retrofit对Rxjava的支持需要在Gradle文件中添加

1
compile 'io.reactivex:rxjava:1.1.6'

上文中已经添加

然后在创建Retrofit的过程中添加如下代码:

1
2
3
4
5
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();

这样一来我们定义的service返回值就不在是一个Call了,而是一个Observable

重新定义 JokeService 方法

1
2
3
4
5
6
7
8
9
/**
* 请求示例:http://japi.juhe.cn/joke/content/list.from?
* key=您申请的KEY&page=2&pagesize=10&sort=asc&time=1418745237
*/
public interface JokeService {
@GET("content/list.from")
Observable<HttpJokeResult<JokeEntity.Data>> getJokes(@QueryMap Map<String, Object> options);
}

getJokes方法改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/**
* Retrofit + RxJava
* @param options
*/
private void getJokes2(Map<String, Object> options) {
String baseUrl="http://japi.juhe.cn/joke/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
JokeService jokeService = retrofit.create(JokeService.class);
jokeService.getJokesByRxJava(options)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<JokeEntity>() {
@Override
public void onCompleted() {
Toast.makeText(mContext, "Get Top Joke Completed", Toast.LENGTH_SHORT).show();
}
@Override
public void onError(Throwable e) {
textview.setText(e.getMessage());
}
@Override
public void onNext(JokeEntity jokeEntity) {
Logger.i(jokeEntity.toString());
if(jokeEntity.getError_code()==0) {
List<JokeEntity.Data.Subject> data = jokeEntity.getResult().getData();
for (JokeEntity.Data.Subject subject : data) {
Logger.i(subject.toString());
}
textview.setText(data.get(0).toString());
}else {
textview.setText(jokeEntity.toString());
}
}
});
}

这样基本上就完成了Retrofit和Rxjava的结合,但是这样封装还是不太满意。

接下来我们把创建Retrofit的过程封装一下,然后希望Activity/Fargment创建Subscriber对象传进来。

将请求过程进行封装

创建一个对象HttpMethods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
package com.javen.smartcloud.http;
import com.javen.smartcloud.entity.HttpJokeResult;
import com.javen.smartcloud.entity.JokeEntity;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
import rx.Observable;
import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Func1;
import rx.schedulers.Schedulers;
public class HttpMethods {
public static final String BASE_URL = "http://japi.juhe.cn/joke/";
private static final int DEFAULT_TIMEOUT = 5;
private Retrofit retrofit;
private JokeService jokeService;
//构造方法私有
private HttpMethods() {
//手动创建一个OkHttpClient并设置超时时间
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
httpClientBuilder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
retrofit = new Retrofit.Builder()
.client(httpClientBuilder.build())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.baseUrl(BASE_URL)
.build();
jokeService = retrofit.create(JokeService.class);
}
//在访问HttpMethods时创建单例
private static class SingletonHolder{
private static final HttpMethods INSTANCE = new HttpMethods();
}
//获取单例
public static HttpMethods getInstance(){
return SingletonHolder.INSTANCE;
}
/**
* 用来统一处理Http的resultCode,并将HttpResult的Data部分剥离出来返回给subscriber
*
* @param <T> Subscriber真正需要的数据类型,也就是Data部分的数据类型
*/
private class HttpResultFunc<T> implements Func1<HttpJokeResult<T>, T> {
@Override
public T call(HttpJokeResult<T> httpResult) {
if (httpResult.getError_code() != 0) {
throw new ApiException(httpResult.getError_code());
}
return httpResult.getResult();
}
}
private <T> void toSubscribe(Observable<T> observable, Subscriber<T> subscriber){
observable.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber);
}
/**
* 用于获取聚合笑话的数据
* @param subscriber 由调用者传过来的观察者对象
* @param options 访问参数
*/
public void getJokes(Subscriber<JokeEntity> subscriber, Map<String, Object> options){
// jokeService.getJokesByRxJava(options)
// .subscribeOn(Schedulers.io())
// .unsubscribeOn(Schedulers.io())
// .observeOn(AndroidSchedulers.mainThread())
// .subscribe(subscriber);
Observable<JokeEntity> observable = jokeService.getJokesByRxJava(options);
toSubscribe(observable,subscriber);
}
}

用一个单例来封装该对象,在构造方法中创建Retrofit和对应的Service。 如果需要访问不同的基地址,那么你可能需要创建多个Retrofit对象,或者干脆根据不同的基地址封装不同的HttpMethod类。

再来看Activity/Fargment中的getJokes方法: 以下代码中的 getJokes3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
package com.javen.smartcloud.fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.javen.smartcloud.R;
import com.javen.smartcloud.entity.HttpJokeResult;
import com.javen.smartcloud.entity.JokeEntity;
import com.javen.smartcloud.http.HttpMethods;
import com.javen.smartcloud.http.JokeService;
import com.javen.smartcloud.subscribers.ProgressSubscriber;
import com.orhanobut.logger.Logger;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
public class FragmentOne extends BaseFragment {
@BindView(R.id.id_joke_tv)
TextView textview;
@BindView(R.id.id_joke_btn)
Button jokeBtn;
Subscriber<JokeEntity> subscriber;
Subscriber<HttpJokeResult<JokeEntity.Data>> subscriber2;
Subscriber<JokeEntity.Data> subscriber3;
public FragmentOne() {
}
@Override
public View initView(LayoutInflater inflater, ViewGroup container) {
View view = inflater.inflate(R.layout.fragment_one, container, false);
ButterKnife.bind(this, view);
return view;
}
@Override
public void initData() {
}
@OnClick(R.id.id_joke_btn)
public void onClick() {
long timeMillis = System.currentTimeMillis();
String time=String.valueOf(timeMillis).substring(0,10);
Logger.e("当前时间戳:"+timeMillis+" 前10位:"+time);
Toast.makeText(mContext, "点我", Toast.LENGTH_SHORT).show();
Map<String,Object> options = new HashMap<String, Object>();
options.put("key","bd19bfd8cb856884b5739b0267ec24c9");
options.put("sort","desc");
options.put("page",1);
options.put("pagesize",2);
options.put("time",time);
// getJokes1(options);
// getJokes2(options);
// getJokes3(options);
}
/**
* Retrofit + RxJava 封装版
* 3.添加HttpMethods
* @param options
*/
private void getJokes3(Map<String, Object> options){
subscriber = new Subscriber<JokeEntity>() {
@Override
public void onCompleted() {
Toast.makeText(mContext, "Get Top Joke Completed", Toast.LENGTH_SHORT).show();
}
@Override
public void onError(Throwable e) {
textview.setText(e.getMessage());
}
@Override
public void onNext(JokeEntity jokeEntity) {
Logger.i(jokeEntity.toString());
if(jokeEntity.getError_code()==0) {
List<JokeEntity.Data.Subject> data = jokeEntity.getResult().getData();
for (JokeEntity.Data.Subject subject : data) {
Logger.i(subject.toString());
}
textview.setText(data.get(0).toString());
}else {
textview.setText(jokeEntity.toString());
}
}
};
HttpMethods.getInstance().getJokes(subscriber,options);
}
/**
* Retrofit + RxJava
* @param options
*/
private void getJokes2(Map<String, Object> options) {
String baseUrl="http://japi.juhe.cn/joke/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
JokeService jokeService = retrofit.create(JokeService.class);
jokeService.getJokesByRxJava(options)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<JokeEntity>() {
@Override
public void onCompleted() {
Toast.makeText(mContext, "Get Top Joke Completed", Toast.LENGTH_SHORT).show();
}
@Override
public void onError(Throwable e) {
textview.setText(e.getMessage());
}
@Override
public void onNext(JokeEntity jokeEntity) {
Logger.i(jokeEntity.toString());
if(jokeEntity.getError_code()==0) {
List<JokeEntity.Data.Subject> data = jokeEntity.getResult().getData();
for (JokeEntity.Data.Subject subject : data) {
Logger.i(subject.toString());
}
textview.setText(data.get(0).toString());
}else {
textview.setText(jokeEntity.toString());
}
}
});
}
/**
* 原生的Retrofit请求
*/
private void getJokes1(Map<String, Object> options) {
String baseUrl="http://japi.juhe.cn/joke/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build();
JokeService jokeService = retrofit.create(JokeService.class);
Call<JokeEntity> call = jokeService.getJokes(options);
call.enqueue(new Callback<JokeEntity>() {
@Override
public void onResponse(Call<JokeEntity> call, Response<JokeEntity> response) {
JokeEntity jokeEntity = response.body();
Logger.i(jokeEntity.toString());
if(jokeEntity.getError_code()==0) {
List<JokeEntity.Data.Subject> data = jokeEntity.getResult().getData();
for (JokeEntity.Data.Subject subject : data) {
Logger.i(subject.toString());
}
textview.setText(data.get(0).toString());
}
}
@Override
public void onFailure(Call<JokeEntity> call, Throwable t) {
textview.setText(t.getMessage());
}
});
}
@Override
public void onDestroy() {
super.onDestroy();
if (subscriber!=null){
subscriber.unsubscribe();
}
if (subscriber2!=null){
subscriber2.unsubscribe();
}
if (subscriber3!=null){
subscriber3.unsubscribe();
}
}
}

相同格式的Http请求数据该如何封装

参考资料 RxJava+Retrofit,在联网返回后如何先进行统一的判断?

查看封装最终结果 选择Tag2

实现聚合笑话加载刷新 选择Tag3

实现干货网福利(图片)接口 选择Tag4

效果图展示

girls
长按效果图
点击效果图
点击效果图2
Javen wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!
坚持原创技术分享,您的支持将鼓励我继续创作!